//@author A0096723X
#include "EntriesDisplay.h"

const QString EntriesDisplay::OUTPUT_FORMAT_DATE = "ddd, dd MMM ";
const QString EntriesDisplay::OUTPUT_FORMAT_DATE_TO = "-ddd, dd MMM ";
const QString EntriesDisplay::OUTPUT_FORMAT_TIME_TO = "-hh:mmap";
const QString EntriesDisplay::OUTPUT_FORMAT_TIME_BR = "hh:mmap <br>";
const QString EntriesDisplay::OUTPUT_FORMAT_TIME =  "hh:mmap";
const QString EntriesDisplay::OUTPUT_FORMAT_TIME_TASK =  "by hh:mmap";
const QString EntriesDisplay::OUTPUT_FORMAT_DATE_TASK = "by ddd, dd MMM ";
const string EntriesDisplay::OUTPUT_FORMAT_TODAY_FROM = "Today ";
const string EntriesDisplay::OUTPUT_FORMAT_TODAY_TO = "-Today ";
const string EntriesDisplay::OUTPUT_MSG_NO_ENTRIES = "~No Entries~";
const string EntriesDisplay::STRING_EMPTY = "";

const char* EntriesDisplay::OUTPUT_MSG_PAST_ENTRY = "%s. %s%s";
const char* EntriesDisplay::OUTPUT_MSG_TODAY_ENTRY = "%s. %s";
const char* EntriesDisplay::OUTPUT_MSG_BR_IN_DAYS = "<br>[in %d days]";
const char* EntriesDisplay::OUTPUT_MSG_BR_OVER_BY_DAYS = "<br>[over by %d days]";
const char* EntriesDisplay::STRING_HEADER_SEARCH = "Search %s:";
const char* EntriesDisplay::STRING_HEADER_DATE_SEARCH = "Date Searched:";
const char* EntriesDisplay::OUTPUT_MSG_SEARCH_NO_ITEMS= "No items that fufil search criteria: %s";

EntriesDisplay::EntriesDisplay(Entry* newEntry, vector<Entry*> conflictVector){
	_newEntry = newEntry;
	_conflictVector = conflictVector;
}

EntriesDisplay::~EntriesDisplay(void)
{
}

vector<vector<CommonEnum::ReturnString>> EntriesDisplay::getDefaultString(vector<Entry*> entries) {
	vector<vector<CommonEnum::ReturnString>> output;
	vector<CommonEnum::ReturnString> past;
	vector<CommonEnum::ReturnString> today;
	vector<CommonEnum::ReturnString> tomorrow;	
	vector<CommonEnum::ReturnString> future;

	setDefaultDisplayHeader(past, today, tomorrow, future);

	for(int i=0; i<entries.size(); i++) {
		if(EntriesSorter::isPast(entries[i])) {
			//if past
			CommonEnum::ReturnString entryString = getPastString(entries[i], i);
			setFormat(entries[i], entryString);
			past.push_back(entryString);
		}else if(EntriesSorter::isToday(entries[i])) {
			//occurs today or is Float
			CommonEnum::ReturnString entryString = getTodayString(entries[i], i);
			setFormat(entries[i], entryString);
			today.push_back(entryString);
		}else if(EntriesSorter::isTomorrow(entries[i])) {
			//occurs tomorrow
			CommonEnum::ReturnString entryString = getTodayString(entries[i], i);
			setFormat(entries[i], entryString);
			tomorrow.push_back(entryString);
		} else {
			//is future event
			CommonEnum::ReturnString entryString = getPastString(entries[i], i);
			setFormat(entries[i], entryString);
			future.push_back(entryString);
		}
	}
	if(past.size() >1) {
		//display past header only if not empty
		output.push_back(past);
	}
	if(today.size() ==1) {
		appendEmptyEntry(today);
	}	
	if(tomorrow.size() ==1) {
		appendEmptyEntry(tomorrow);
	}	
	if(future.size() ==1) {
		appendEmptyEntry(future);
	}
	
	output.push_back(today);
	output.push_back(tomorrow);
	output.push_back(future);
	return output;
}
vector<vector<CommonEnum::ReturnString>> EntriesDisplay::getSearchString(vector<Entry*> entries, string searchLabel) {
	vector<vector<CommonEnum::ReturnString>> output;
	vector<CommonEnum::ReturnString> search;
	setSearchDisplayHeader(search, searchLabel);
	for(int i=0; i<entries.size(); i++) {
		CommonEnum::ReturnString entryString = getPastString(entries[i], i);
		setFormat(entries[i], entryString);
		search.push_back(entryString);
	}
	if(search.size() == 1) {
		appendEmptyEntry(search);
	}
	output.push_back(search);
	return output;
}
vector<vector<CommonEnum::ReturnString>> EntriesDisplay::getSearchDateString(vector<Entry*> entries, string searchDateString) {
	vector<vector<CommonEnum::ReturnString>> output;
	vector<CommonEnum::ReturnString> searchDate;
	setSearchDateHeader(searchDate, searchDateString);
	for(int i=0; i<entries.size(); i++) {
		CommonEnum::ReturnString entryString = getPastString(entries[i], i);
		setFormat(entries[i], entryString);
		searchDate.push_back(entryString);
	}
	if(searchDate.size() ==1) {
		appendEmptyEntry(searchDate);
	}
	output.push_back(searchDate);
	return output;
}


//Helper Functions below
CommonEnum::ReturnString EntriesDisplay::getPastString(Entry* entry, int index) {
	CommonEnum::ReturnString entryString;
	char buffer[MAX_SIZE];
	sprintf_s(buffer, OUTPUT_MSG_PAST_ENTRY, to_string(index+1).c_str(), entry->getName().c_str(), getDaysTilString(entry).c_str() );
	entryString.entry.first= buffer;
	if(EntriesSorter::isTask(entry)){
		entryString.entry.second = printTaskDateTime(entry);
	}else{
		entryString.entry.second = printEventDateTime(entry);
	}
	return entryString;
}
CommonEnum::ReturnString EntriesDisplay::getTodayString(Entry* entry, int index) {
	CommonEnum::ReturnString entryString;
	char buffer[MAX_SIZE];
	sprintf_s(buffer, OUTPUT_MSG_TODAY_ENTRY, to_string(index+1).c_str(), entry->getName().c_str());
	entryString.entry.first= buffer;
	if(entry->getEntryType() == CommonUtils::ENTRY_TYPE_TASK){
		entryString.entry.second = printTodayTaskDateTime(entry);
	}else{
		if(entry->isSpanning()){
			entryString.entry.second =  printTodaySpanningEventDateTime(entry);
		}else {
			entryString.entry.second =  printTodayEventDateTime(entry);
		}
	}
	return entryString;
}

void EntriesDisplay::appendEmptyEntry(vector<CommonEnum::ReturnString> &displayVector){
	
	CommonEnum::ReturnString emptyString;

	emptyString.entry.first = OUTPUT_MSG_NO_ENTRIES;
	emptyString.entry.second = STRING_EMPTY;
	emptyString.format = CommonEnum::DISPLAY_FORMAT_DEFAULT;
	displayVector.push_back(emptyString);
}
void EntriesDisplay::setFormat(Entry* entry, CommonEnum::ReturnString &entryString) {
	if(entry->getStatus() == true) {
		entryString.format=CommonEnum::DISPLAY_FORMAT_COMPLETED;
	} else if(entry == _newEntry){
		entryString.format=CommonEnum::DISPLAY_FORMAT_NEW;
	} else if(find(_conflictVector.begin(),_conflictVector.end(),entry) != _conflictVector.end()) {//if found in conflict
		entryString.format=CommonEnum::DISPLAY_FORMAT_CLASH;
	} else {
		entryString.format=CommonEnum::DISPLAY_FORMAT_DEFAULT;
	}
}
void EntriesDisplay::setDefaultDisplayHeader(vector<CommonEnum::ReturnString>& past,vector<CommonEnum::ReturnString>& today,vector<CommonEnum::ReturnString>& tomorrow,vector<CommonEnum::ReturnString>& future){
	
	CommonEnum::ReturnString pastHeader;

	pastHeader.entry.first= CommonUtils::STRING_HEADER_OVERDUE;
	pastHeader.entry.second=STRING_EMPTY;
	pastHeader.format = CommonEnum::DISPLAY_FORMAT_OVERDUE;
	past.push_back(pastHeader);	

	CommonEnum::ReturnString todayHeader;

	todayHeader.entry.first= CommonUtils::STRING_HEADER_TODAY;
	todayHeader.entry.second= QDate::currentDate().toString(CommonUtils::FORMAT_DATE_HEADER).toStdString();
	todayHeader.format = CommonEnum::DISPLAY_FORMAT_TODAY;
	today.push_back(todayHeader);

	CommonEnum::ReturnString tomorrowHeader;

	tomorrowHeader.entry.first= CommonUtils::STRING_HEADER_TOMORROW;
	tomorrowHeader.entry.second=QDate::currentDate().addDays(1).toString(CommonUtils::FORMAT_DATE_HEADER).toStdString();
	tomorrowHeader.format = CommonEnum::DISPLAY_FORMAT_DEFAULT;
	tomorrow.push_back(tomorrowHeader);

	CommonEnum::ReturnString futureHeader;

	futureHeader.entry.first= CommonUtils::STRING_HEADER_FUTURE;
	futureHeader.entry.second=STRING_EMPTY;
	futureHeader.format = CommonEnum::DISPLAY_FORMAT_DEFAULT;
	future.push_back(futureHeader);
}
void EntriesDisplay::setSearchDisplayHeader(vector<CommonEnum::ReturnString> &displayVector, string searchLabel) {
	
	CommonEnum::ReturnString searchHeader;
	
	char buffer[MAX_SIZE];

	sprintf_s(buffer, STRING_HEADER_SEARCH, searchLabel.c_str());

	searchHeader.entry.first= buffer;
	searchHeader.entry.second=STRING_EMPTY;
	searchHeader.format = CommonEnum::DISPLAY_FORMAT_DEFAULT;
	displayVector.push_back(searchHeader);
}
void EntriesDisplay::setSearchDateHeader(vector<CommonEnum::ReturnString> &displayVector, string searchDateString) {
	
	CommonEnum::ReturnString searchDateHeader;

	searchDateHeader.entry.first= STRING_HEADER_DATE_SEARCH;
	searchDateHeader.entry.second= searchDateString;
	searchDateHeader.format = CommonEnum::DISPLAY_FORMAT_DEFAULT;
	displayVector.push_back(searchDateHeader);
}

string EntriesDisplay::printEventDateTime(Entry* entry) {
	string output;
	output = entry->getStartDate().toString(OUTPUT_FORMAT_DATE).toStdString() + entry->getStartTime().toString(OUTPUT_FORMAT_TIME_BR).toStdString();
	if(entry->getEndDate() != entry->getStartDate()) {//diff dates
		output += entry->getEndDate().toString(OUTPUT_FORMAT_DATE_TO).toStdString()  + entry->getEndTime().toString(OUTPUT_FORMAT_TIME).toStdString();
	} else {
		if((entry->getEndTime() != entry->getStartTime())) {//same date diff time
			output += entry->getEndTime().toString(OUTPUT_FORMAT_TIME_TO).toStdString();
		}
	}
	return output;
}
string EntriesDisplay::printTaskDateTime(Entry* entry) {
	return entry->getEndDate().toString(OUTPUT_FORMAT_DATE_TASK).toStdString() + entry->getEndTime().toString(OUTPUT_FORMAT_TIME).toStdString();
}
string EntriesDisplay::getDaysTilString(Entry* entry) {
	QDate date=entry->getDate();
	QDate currentDate=QDate::currentDate();
	int daysTo;
	if(date.isValid() && date != currentDate){
		daysTo = currentDate.daysTo(date);
	} else{
		return STRING_EMPTY;
	}
	char buffer[MAX_SIZE];
	if(daysTo > 0) {
		sprintf_s(buffer, OUTPUT_MSG_BR_IN_DAYS, daysTo);
	} else if(daysTo < 0) {
		sprintf_s(buffer, OUTPUT_MSG_BR_OVER_BY_DAYS, abs(daysTo));
	}
	return buffer;
}
string EntriesDisplay::printTodayTaskDateTime(Entry* entry) {
	return entry->getEndTime().toString(OUTPUT_FORMAT_TIME_TASK).toStdString();
}
string EntriesDisplay::printTodayEventDateTime(Entry* entry) {
	return entry->getStartTime().toString(OUTPUT_FORMAT_TIME_BR).toStdString() + 
		(entry->getEndTime() != entry->getStartTime() ?//same date diff time
		entry->getEndTime().toString(OUTPUT_FORMAT_TIME_TO).toStdString() ://print time
		STRING_EMPTY);
}
string EntriesDisplay::printTodaySpanningEventDateTime(Entry* entry) {
	QDate startDate = entry->getStartDate();
	string startDateString;
	if(startDate == QDate::currentDate()) {
		startDateString = OUTPUT_FORMAT_TODAY_FROM;
	} else {
		startDateString = startDate.toString(OUTPUT_FORMAT_DATE).toStdString();
	}
	QDate endDate = entry->getEndDate();
	string endDateString;
	if(endDate == QDate::currentDate()) {
		endDateString = OUTPUT_FORMAT_TODAY_TO;
	} else {
		endDateString = endDate.toString(OUTPUT_FORMAT_DATE_TO).toStdString();
	}
	return (startDateString + entry->getStartTime().toString(OUTPUT_FORMAT_TIME_BR).toStdString() 
		+ endDateString + entry->getEndTime().toString(OUTPUT_FORMAT_TIME).toStdString());
}